package com.fuchuang.seckillsystem.service.impl;

import com.fuchuang.seckillsystem.dto.KeyConstants;
import com.fuchuang.seckillsystem.dto.OrderInfo;
import com.fuchuang.seckillsystem.dto.OrderQuery;
import com.fuchuang.seckillsystem.dto.TimeConstants;
import com.fuchuang.seckillsystem.entity.Account;
import com.fuchuang.seckillsystem.entity.AccountFlow;
import com.fuchuang.seckillsystem.entity.Goods;
import com.fuchuang.seckillsystem.entity.Order;
import com.fuchuang.seckillsystem.exception.GlobalException;
import com.fuchuang.seckillsystem.mapper.AccountFlowMapper;
import com.fuchuang.seckillsystem.mapper.OrderMapper;
import com.fuchuang.seckillsystem.rocketmq.OrderProducer;
import com.fuchuang.seckillsystem.service.IAccountService;
import com.fuchuang.seckillsystem.service.IGoodService;
import com.fuchuang.seckillsystem.service.IOrderService;
import com.fuchuang.seckillsystem.service.ISeckillService;
import com.fuchuang.seckillsystem.vo.RespBeanEnum;
import com.fuchuang.seckillsystem.vo.SeckillMessage;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private IGoodService goodService;
    
    @Autowired
    private ISeckillService seckillService;
    
    @Autowired
    private IAccountService accountService;
    
    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    
    @Autowired
    TransactionDefinition transactionDefinition;
    
    @Resource
    private OrderMapper orderMapper;
    
    @Resource
    private AccountFlowMapper accountFlowMapper;
    
    @Autowired
    private OrderProducer orderProducer;
    
    @Override
    @Transactional
    public void createSeckillOrder(SeckillMessage seckillMessage) {
        //获取mq中的数据
        Integer goodsId = seckillMessage.getGoodsId();
        Integer activityId = seckillMessage.getActivityId();
        Integer userId = seckillMessage.getUserId();
        
        //从redis中获取商品信息
        Goods goods = goodService.selectGoodsById(goodsId);
        
        //生产订单信息
        Order order = new Order();
        order.setUserId(userId).setGoodsId(goodsId).setActivityId(activityId);
        order.setPayPrice(goods.getPrice()).setPayStatus(0);
        Date date = new Date();
        order.setCreateTime(date).setUpdateTime(date);
        
        //判断时哪里异常
        int exceptionFlag = 0;
        //手动开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        try {
            //递减数据库库存
            goodService.decreaseStock(goodsId);
            exceptionFlag = 1;
            //生产订单
            orderMapper.insertSelective(order);
            //将订单存入延迟队列中
            orderProducer.sendExpireOrderMessage(order.getId());
            //设置抢购成功的标识
            redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + activityId).put(KeyConstants.SECKILL_SUCCESS + goodsId + ":" + userId, order.getId());
            //将订单存入redis中, 设置过期比延迟队列晚一些
            redisTemplate.opsForValue().set(KeyConstants.ORDER + order.getId(), order, TimeConstants.ORDER, TimeUnit.MINUTES);
            //删除用户之前可能购买失败的记录
            redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + activityId).delete(KeyConstants.SECKILL_FAILURE + goodsId + ":" + userId);
            //提交事务
            dataSourceTransactionManager.commit(transactionStatus);
        } catch (Exception e) {
            //回滚事务
            dataSourceTransactionManager.rollback(transactionStatus);
            //解锁，设置抢购失败的标识
            redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + activityId).delete(KeyConstants.SECKILL_REPEAT + goodsId + ":" + userId);
            redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + activityId).put(KeyConstants.SECKILL_FAILURE + goodsId + ":" + userId, false);
            //判断异常是因为库存还是订单，如果是生产订单产生则回滚redis的库存
            if (exceptionFlag == 1) {
                redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + activityId).increment(KeyConstants.SECKILL_GOODS_STOCK + goodsId, 1);
                //设置jvm库存为false
                seckillService.setIsEmpty(activityId, goodsId, false);
            }
        }
    }
    
    @Override
    @Transactional
    public void pay(Integer orderId, Integer userId) {
        Order order = getOrderFromRedis(orderId);
        judgeOrderAuth(order, userId);
        //判断用户余额是否足够
        Account account = accountService.getAccountByUserId(userId);
        if (account.getBalance().compareTo(order.getPayPrice()) < 0) {
            throw new GlobalException(RespBeanEnum.ACCOUNT_MONEY_ERROR);
        }
        //扣除金额
        account.setBalance(account.getBalance().subtract(order.getPayPrice()));
        account.setUpdateTime(new Date());
        //更新用户账户余额
        accountService.updateAccount(account);
        //更新银行账户余额
        accountService.updateBankAccount(order.getPayPrice());
        //补充参数
        order.setPayTime(new Date());
        order.setUpdateTime(new Date());
        order.setPayStatus(1);
        //插入到数据库中
        int rows = orderMapper.updateByPrimaryKeySelective(order);
        if (rows != 1) {
            throw new GlobalException(RespBeanEnum.ORDER_PAY_ERROR);
        }
        //更新redis中order信息
        updateRedisOrder(order);
        //创建订单流水信息
        AccountFlow accountFlow = createAccountFlow(userId, order);
        rows = accountFlowMapper.insertSelective(accountFlow);
        if (rows != 1) {
            throw new GlobalException(RespBeanEnum.ORDER_PAY_ERROR);
        }
    }
    
    /**
     * 创建订单流水信息
     */
    private AccountFlow createAccountFlow(Integer userId, Order order) {
        //生成账单流水信息
        AccountFlow accountFlow = new AccountFlow();
        accountFlow.setAmount(order.getPayPrice());
        accountFlow.setPayerId(userId);
        accountFlow.setPayeeId(0);
        accountFlow.setIsValid(1);
        accountFlow.setOrderId(order.getId());
        Date date = new Date();
        accountFlow.setCreateTime(date);
        accountFlow.setUpdateTime(date);
        return accountFlow;
    }
    
    /**
     * 从redis中获取order信息
     */
    private Order getOrderFromRedis(Integer orderId) {
        Order order = (Order) redisTemplate.opsForValue().get(KeyConstants.ORDER + orderId);
        if (order == null) {
            throw new GlobalException(RespBeanEnum.ORDER_NOT_EXIST_ERROR);
        }
        return order;
    }
    
    /**
     * 判断订单是否由对应用户操作
     */
    private void judgeOrderAuth(Order order, Integer userId) {
        //如果不是对应用户操作订单
        if (!order.getUserId().equals(userId)) {
            throw new GlobalException(RespBeanEnum.ORDER_AUTH_ERROR);
        }
    }
    
    @Override
    @Transactional
    public void cancelOrder(Integer orderId, Integer userId) {
        Order order = getOrderFromRedis(orderId);
        if (order.getPayStatus() == 1) {
            throw new GlobalException(RespBeanEnum.ORDER_CANCEL_ERROR);
        }
        judgeOrderAuth(order, userId);
        //更新订单状态
        order.setUpdateTime(new Date());
        order.setPayStatus(-1);
        int rows = orderMapper.updateByPrimaryKeySelective(order);
        if (rows != 1) {
            throw new GlobalException(RespBeanEnum.ORDER_CANCEL_ERROR);
        }
        //删除redis中的订单信息
        updateRedisOrder(order);
        //回滚库存
        rollBackStock(order);
        //删除购买记录
        redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + order.getActivityId()).delete(KeyConstants.SECKILL_REPEAT + order.getGoodsId() + ":" + order.getUserId());
    }
    
    @Override
    public void orderExpire(Integer orderId) {
        Order order = (Order) redisTemplate.opsForValue().get(KeyConstants.ORDER + orderId);
        if (order == null) {
            return;
        }
        //判断支付状态，如果已支付则不做处理
        if (order.getPayStatus() == 1) {
            return;
        }
        //如果没有支付，更新order状态
        order.setPayStatus(-1);
        order.setUpdateTime(new Date());
        try {
            orderMapper.updateByPrimaryKeySelective(order);
            redisTemplate.delete(KeyConstants.ORDER + orderId);
            //删除购买记录
            redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + order.getActivityId()).delete(KeyConstants.SECKILL_REPEAT + order.getGoodsId() + ":" + order.getUserId());
            rollBackStock(order);
        } catch (Exception e) {
            log.info("id为：{}的订单过期处理失败", orderId);
        }
    }
    
    /**
     * 回滚库存
     */
    private void rollBackStock(Order order) {
        Integer activityId = order.getActivityId();
        Integer goodsId = order.getGoodsId();
        Integer userId = order.getUserId();
        
        //数据库回滚库存
        goodService.increaseStock(goodsId);
        //判断活动是否结束
        Boolean hasKey = redisTemplate.hasKey(KeyConstants.SECKILL_ACTIVITY + activityId);
        if (hasKey != null && hasKey) {
            //redis中回滚库存
            redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + activityId).increment(KeyConstants.SECKILL_GOODS_STOCK + goodsId, 1);
            //jvm售空标识为false
            seckillService.setIsEmpty(activityId, goodsId, false);
            //解锁
            redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + activityId).delete(KeyConstants.SECKILL_REPEAT + goodsId + ":" + userId);
            //删除抢购成功的标识
            redisTemplate.boundHashOps(KeyConstants.SECKILL_ACTIVITY + activityId).delete(KeyConstants.SECKILL_SUCCESS + order.getGoodsId() + ":" + order.getUserId());
        }
    }
    
    /**
     * 更新redis中抢购成功信息
     */
    private void updateRedisOrder(Order order) {
        redisTemplate.opsForValue().set(KeyConstants.ORDER + order.getId(), order, TimeConstants.ORDER, TimeUnit.MINUTES);
    }
    
    @Override
    public OrderInfo getOrderDetail(Integer orderId) {
        if (orderId == null || orderId <= 0) {
            throw new GlobalException(RespBeanEnum.PARAMS_ERROR);
        }
        OrderInfo orderInfo = orderMapper.queryOrderInfoByOrderId(orderId);
        if (orderInfo == null) {
            throw new GlobalException(RespBeanEnum.ORDER_NOT_EXIST_ERROR);
        }
        return orderInfo;
    }
    
    @Override
    public Map<String, Object> getUserOrderInfo(OrderQuery orderQuery) {
        return getOrderInfoList(orderQuery);
    }
    
    @Override
    public Map<String, Object> getAllOrderInfo(OrderQuery orderQuery) {
        return getOrderInfoList(orderQuery);
    }
    
    private Map<String, Object> getOrderInfoList(OrderQuery orderQuery) {
        Map<String, Object> map = new HashMap<>();
        PageHelper.startPage(orderQuery.getPage(), orderQuery.getLimit());
        PageInfo<OrderInfo> pageInfo;
        if (orderQuery.getUserId() != null) {
            pageInfo = new PageInfo<>(orderMapper.queryUserOrderInfo(orderQuery));
        } else {
            pageInfo = new PageInfo<>(orderMapper.queryAllOrderInfo(orderQuery));
        }
        map.put("list", pageInfo.getList());
        map.put("count", pageInfo.getTotal());
        return map;
    }
}
